import sys
from random import random, randint

import pygame
from pygame import FULLSCREEN

from alien import Alien
from bullet import Bullet
from button import Button
from game_stats import GameStats
from score_board import ScoreBoard
from settings import Settings
from ship import Ship


class AlienInvasion:
    """外星人入侵管理类"""

    def __init__(self):
        # 初始化需要的pygame
        pygame.init()
        # 导入游戏设置
        self.settings = Settings()
        # 绘制屏幕大小
        self.screen = pygame.display.set_mode((0, 0), FULLSCREEN)
        self.settings.width = self.screen.get_rect().width
        self.settings.height = self.screen.get_rect().height
        # 初始化
        self.game_stats = GameStats(self)
        # 初始飞船对象
        self.ship = Ship(self)
        # 编组存储子弹
        self.bullets = pygame.sprite.Group()
        # 编组并创建外星人
        self.aliens = pygame.sprite.Group()
        self._create_fleet()
        # 创建按钮
        self.button = Button(self, "Start")
        # 记分牌
        self.score_board = ScoreBoard(self)
        # 加载子弹发射音效
        self.fire_voice = pygame.mixer.Sound('voice/fire.wav')
        # 加载背景
        self.bg = pygame.transform.scale(pygame.image.load('images/bg.png'),
                                         (self.settings.width, self.settings.height))
        # 加载背景音乐
        pygame.mixer.music.load('voice/bg.mp3')
        pygame.display.set_caption("Alien Invasion")

    def _create_fleet(self):
        """ 创建外星人舰队,每次随机位置生成指定数量 """
        # 创建一个外星人，获取其宽、高
        alien = Alien(self)
        alien_width = alien.rect.width

        for num in range(int(self.settings.alien_nums)):
            alien = Alien(self)
            alien.x = randint(alien_width, self.settings.width - alien_width)
            alien.rect.x = alien.x
            self.aliens.add(alien)

    def run_game(self):
        # 播放背景音乐
        pygame.mixer.music.play(-1)
        while True:
            # 监听键盘事件
            self._monitor_event()
            # 如果游戏结束了，不执行
            if self.game_stats.game_active:
                # 飞船更新
                self.ship.move()
                # 子弹更新
                self._update_bullet()
                # 外星人更新
                self._update_alien()
            # 刷新屏幕
            self._refresh_screen()

    def _monitor_event(self):
        """监听键盘事件"""
        for event in pygame.event.get():
            # 退出事件
            if event.type == pygame.QUIT:
                sys.exit()
            # 键盘按下事件
            elif event.type == pygame.KEYDOWN:
                self._monitor_keydown_event(event)
            # 键盘松开事件
            elif event.type == pygame.KEYUP:
                self._monitor_keyup_event(event)
            # 按钮点击事件
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                self._check_play_button(mouse_pos)

    def _monitor_keydown_event(self, event):
        """监听键盘按下事件"""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        if event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        if event.key == pygame.K_UP:
            self.ship.moving_top = True
        if event.key == pygame.K_DOWN:
            self.ship.moving_bottom = True
        if event.key == pygame.K_q:
            sys.exit()
        if event.key == pygame.K_SPACE:
            self.fire_voice.play()
            self._fire_bullet()

    def _fire_bullet(self):
        """ 开火 """
        self.bullets.add(Bullet(self))

    def _monitor_keyup_event(self, event):
        """监听键盘松开事件"""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        if event.key == pygame.K_LEFT:
            self.ship.moving_left = False
        if event.key == pygame.K_UP:
            self.ship.moving_top = False
        if event.key == pygame.K_DOWN:
            self.ship.moving_bottom = False

    def _update_bullet(self):
        # 子弹移动
        self.bullets.update()
        # 删除屏幕之外的位置
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)
        # 碰撞检测及处理
        self._check_bullet_alien_collision()

    def _check_bullet_alien_collision(self):
        """ 碰撞检测及处理 """
        # 碰撞检测
        collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
        # 记录得分
        if collisions:
            for aliens in collisions.values():
                self.game_stats.score += self.settings.alien_score * len(aliens)
            self.score_board.pre_score()
            self.score_board.check_high_score()
        # 如果外星人全部消灭，就重新创建一批
        if not self.aliens:
            self.bullets.empty()
            self._create_fleet()
            # 提高难度
            self.settings.increase_difficult()
            # 增加等级
            self.game_stats.level += 1
            self.score_board.pre_level()

    def _refresh_screen(self):
        """ 刷新屏幕 """
        # 填充屏幕背景
        self.screen.blit(self.bg, (0, 0))
        # 非运行态，展示按钮
        if not self.game_stats.game_active:
            self.button.draw_button()
        else:
            # 绘制飞船位置
            self.ship.blit_ship()
            # 绘制子弹的位置
            for bullet in self.bullets.sprites():
                bullet.create_bullet()
            # 绘制外星人
            self.aliens.draw(self.screen)
            # 绘制记分牌
            self.score_board.show_score()
        # 刷新屏幕
        pygame.display.flip()

    def _update_alien(self):
        """ 更新外星人 """
        # 检查是否达到边缘
        self._check_alien_edge()
        # 外星人移动
        self.aliens.update()
        # 飞船外星人碰撞
        if pygame.sprite.spritecollideany(self.ship, self.aliens):
            self._ship_hit()
        # 外星人到达底部
        self._check_alien_to_bottom()

    def _change_fleet_direction(self):
        """ 改变舰队方向 """
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _ship_hit(self):
        """ 检查飞船和外星人碰撞 """
        if self.game_stats.ship_life > 0:
            # 命数减1
            self.game_stats.ship_life -= 1
            self.score_board.pre_ships()
            # 移除剩余的外星人、子弹
            self.bullets.empty()
            self.aliens.empty()
            # 重新创建外星人、飞船
            self._create_fleet()
            self.ship.center_ship()
            # 暂停，等待玩家重新开始
            self.game_stats.game_active = False
        else:
            # TODO:是否重新启动游戏
            self.game_stats.game_active = False
        # 展示鼠标光标
        pygame.mouse.set_visible(True)
        # 重置难度
        self.settings.init_dynamic_params()
        # 分数清0
        self.game_stats.score = 0
        self.score_board.pre_score()
        # 等级清0
        self.game_stats.level = 1
        self.score_board.pre_level()

    def _check_alien_to_bottom(self):
        """ 检查外星人是否到达底部 """
        screen_rect = self.screen.get_rect()
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom:
                self._ship_hit()
                break

    def _check_play_button(self, mouse_pos):
        """ 检查开始按钮是否被点击 """
        if self.button.rect.collidepoint(mouse_pos) and not self.game_stats.game_active:
            self.game_stats.game_active = True

            self.bullets.empty()
            self.aliens.empty()

            self._create_fleet()
            self.ship.center_ship()

            # 隐藏鼠标光标
            pygame.mouse.set_visible(False)
            # 显示分数
            self.score_board.pre_score()
            # 显示等级
            self.score_board.pre_score()
            # 显示当前命数
            self.score_board.pre_ships()

    def _check_alien_edge(self):
        """ 若达到边缘就改变方向 """
        for alien in self.aliens.sprites():
            if alien.rect.right >= self.settings.width or alien.rect.left <= 0:
                alien.direction *= -1


if __name__ == '__main__':
    alien = AlienInvasion()
    alien.run_game()
